﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Algobox.JuicyLib.TimeSeries;

namespace Algobox.JuicyLib.Analysis
{
    public class SimpleMovingAverage
    {
        protected const int IGNORE = -1;

        /// <summary>
        /// max record count
        /// </summary>
        protected int maxRecords = IGNORE;

        /// <summary>
        /// max record count
        /// </summary>
        public int MaxRecords
        {
            get { return maxRecords; }
            set { maxRecords = value; }
        }

        public bool Primed
        {
            get
            {
                if (maxRecords == IGNORE)
                    return true;
                else
                    if (maxRecords == timeseries.Count)
                        return true;
                return false;
            }
        }

        int intervalInSeconds = 60;

        public int IntervalInSeconds
        {
            get { return intervalInSeconds; }
            set { intervalInSeconds = value; }
        }

        /// <summary>
        /// timeseries of doubles (ticks)
        /// </summary>
        protected BaseTimeSeries timeseries = new BaseTimeSeries();

        /// <summary>
        /// will do a shallow copy then 
        /// </summary>
        /// <param name="ts"></param>
        /// <returns></returns>
        public virtual double? Initialise(BaseTimeSeries ts)
        {
            ts.ShallowCopyBTS(timeseries);
            int count = timeseries.Count;
            
            //do a copy and remove the elements after we have copied the data 
            //not efficient if the alot of data - to do in the base timeseries 
            if (maxRecords != IGNORE && count > maxRecords)
            {
                ts.RemoveRange(0, count - maxRecords);
            }

            return CalculateMovingAverage();
        }

        public void Deinitialise()
        {
            timeseries.Clear(); 
        }

        /// <summary>
        /// get latest MA value from cached values
        /// </summary>
        public double? MA
        {
            get
            {
                if(currentTotal > 0 && timeseries.Count > 0)
                    return currentTotal / timeseries.Count;
                return null;
            }
        }

        /// <summary>
        /// will recalc the entire list + after adding new tick
        /// </summary>
        /// <param name="record">value to insert</param>
        /// <returns></returns>
        public double? AddTickandRecalcMA(BaseTimeSeriesRecord record)
        {
            timeseries.Add(record);
            return CalculateMovingAverage();
        }

        protected double currentTotal = 0;

        public virtual double AddTickandCalcMA(BaseTimeSeriesRecord record)
        {
            return AddTickandCalcMA(record, 0);
        }

        /// <summary>
        /// will recal moving average will remove upto the internal list count , if over will ignore
        /// </summary>
        /// <param name="record">value to insert</param>
        /// <param name="remove">element to remove from head</param>
        /// <returns></returns>
        public virtual double AddTickandCalcMA(BaseTimeSeriesRecord record, int remove)
        {
            double total = currentTotal + record.RecordValue;

            if (remove > 0 && remove <= timeseries.Count)
            {
                BaseTimeSeriesRecord[] removeList = timeseries.RemoveTicks(remove);

                if (removeList != null)
                {
                    for (int i = 0; i < removeList.Length ; i++)
                    {
                        total -= removeList[i].RecordValue; 
                    }
                }
            }

            timeseries.Add(record);

            if (total > 0)
            {
                currentTotal = total;
                total = total / (double)timeseries.Count;
            }
            else
            {
                total = currentTotal / (double)timeseries.Count;
            }
            return total;

        }


        public double AddTickandReCalcMA(BaseTimeSeriesRecord record)
        {
            double total = currentTotal + record.RecordValue;

            DateTime remove = record.RecordTime.AddSeconds(-1 * intervalInSeconds);

            BaseTimeSeriesRecord[] removeList = null;

            if (Primed)
            {
                removeList = timeseries.RemoveTicks(remove);

                if (removeList != null)
                {
                    for (int i = 0; i < removeList.Length; i++)
                    {
                        total -= removeList[i].RecordValue;
                    }
                }
            }
            timeseries.Add(record);

            if (total > 0)
            {
                currentTotal = total;
                total = total / (double)timeseries.Count;
            }
            else
            {
                total = currentTotal / (double)timeseries.Count;
            }
            return total;

        }

        /// <summary>
        /// will recal moving average will remove upto the internal list count for record in cache is less than time specified
        /// /// </summary>
        /// <param name="record"></param>
        /// <param name="remove"></param>
        /// <returns></returns>
        public double AddTickandCalcMA(BaseTimeSeriesRecord record, DateTime remove)
        {
            double total = currentTotal + record.RecordValue;

            BaseTimeSeriesRecord[] removeList = null;
    
            if(Primed)
            {
                removeList = timeseries.RemoveTicks(remove);

                if (removeList != null)
                {
                    for (int i = 0; i < removeList.Length; i++)
                    {
                        total -= removeList[i].RecordValue;
                    }
                }
            }
            timeseries.Add(record);

            if (total > 0)
            {
                currentTotal = total;
                total = total / (double)timeseries.Count;
            }
            else
            {
                total = currentTotal / (double)timeseries.Count;
            }
            return total;

        }

        /// <summary>
        /// Recals the entire records in the cache
        /// </summary>
        /// <returns></returns>
        protected double? CalculateMovingAverage()
        {
            double? val = null;

            double temp = 0;
            for (int i = 0; i < timeseries.Count; i++)
            {
                temp += timeseries[i].RecordValue;  
            }

            if (temp > 0)
            {
                currentTotal = temp;
                temp = temp / (double)timeseries.Count;
                val = temp;
               
            }
            
            return val;
        }
        

    }
}
